﻿using System;
using System.Diagnostics.Tracing;
using System.Threading;

namespace Sustainalytics.Utils
{
    [EventSource(Name = "Sustainalytics-Common")]
    public sealed class CommonEventSource : System.Diagnostics.Tracing.EventSource
    {
        private static Lazy<CommonEventSource> _lazyLog = new Lazy<CommonEventSource>(InitLog, LazyThreadSafetyMode.ExecutionAndPublication);
        private static string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;

        public static CommonEventSource Log => _lazyLog.Value;

        [NonEvent]
        private static CommonEventSource InitLog()
        {
            var log = new CommonEventSource();
            log.Ping();

            return log;
        }

        [Event(1, Level = EventLevel.LogAlways)]
        public void Ping()
        {
            if (IsEnabled())
            {
                WriteEvent(1);
            }
        }

        [NonEvent]
        public void Exception(Exception ex)
        {
            if (System.Environment.UserInteractive)
            {
                Console.WriteLine(ex.ToString());
            }
            if (ex != null)
                this.Exception(ex.GetType().ToString(), ex.Message, ex.ToString());
        }

        [Event(2, Level = EventLevel.Error, Message = "{0} found {1}:{2}")]
        public void Exception(string exceptionType, string exceptionMessage, string exceptionInformation, string pName = null)
        {
            if (IsEnabled())
                WriteEvent(2, pName ?? processName, exceptionType, exceptionMessage, exceptionInformation);
        }

        [Event(4, Level = EventLevel.Informational)]
        public void InOutEvent(string controllerName, string httpMethod, int statusCode, string reasonPhrase, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(4, controllerName, httpMethod, statusCode, reasonPhrase, pName ?? processName);
            }
        }

        [Event(5, Level = EventLevel.Verbose)]
        public void ArgumentsEvent(string key, string value, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(5, pName ?? processName, key, value);
            }
        }

        [Event(6, Level = EventLevel.Verbose)]
        public void OuptputEvent(string outputValue, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(6, pName ?? processName, outputValue);
            }
        }

        [Event(7, Level = EventLevel.Verbose, Message = "{0} {4} of {1} into {2}/{3} with args {5} returns {6}")]
        public void MongoEvent(string className, string dbName, string collectionName, string opName, string args, string result, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(7, pName ?? processName, className, dbName, collectionName, opName, args, result);
            }
        }

        [Event(8, Level = EventLevel.Verbose)]
        public void RepositoryEvent(string text, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(8, pName ?? processName, text);
            }
        }

        [Event(9, Level = EventLevel.Verbose, Message = "{0} checks {1}")]
        public void ConnectionStringCheck(string text, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(9, pName ?? processName, text);
            }
        }

        [Event(10, Level = EventLevel.Informational, Message = "{0} updates {2}")]
        public void ConnectionStringUpdate(string process, string connection, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(10, pName ?? processName, process, connection);
            }
        }

        // web request logger
        public static class Keywords   // This is a bitvector
        {
            public const EventKeywords WebRequestInput = (EventKeywords)0x0001;
            public const EventKeywords WebRequestInputArguments = (EventKeywords)0x0002;
            public const EventKeywords WebRequestOutputContent = (EventKeywords)0x0004;

            public const EventKeywords RedisTrack = (EventKeywords)0x0008;
        }

        public bool IsWebRequestInputEnabled()
        {
            return IsEnabled(EventLevel.Verbose, Keywords.WebRequestInput);
        }

        public bool IsWebRequestInputArgumentsEnabled()
        {
            return IsEnabled(EventLevel.Verbose, Keywords.WebRequestInputArguments);
        }

        public bool IsWebRequestOutputContentEnabled()
        {
            return IsEnabled(EventLevel.Verbose, Keywords.WebRequestOutputContent);
        }

        [Event(15, Level = EventLevel.Verbose, Message = "{0}.{1}.{2} received {3}, returned status {4}, reason {5}, args {6} and content {7}, took {8}")]
        public void WebRequestActivity(string ctrlName, string methName, string requestUri, string statusCode, string reasonPhrase, string args, string output, string duration, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(15, ctrlName, methName, requestUri, statusCode, reasonPhrase, args, output, duration, pName ?? processName);
            }
        }

        [Event(20, Level = EventLevel.Verbose, Message = "{2}: to redis key {0}")]
        public void WriteToRedis(string key, string value, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(20, key, GetValue(value), processName);
            }
        }

        private static string GetValue(string value)
        {
            return value.Length > 16000 ? "Value too large!" : value;
        }

        [Event(21, Level = EventLevel.Verbose, Message = "{2} from redis key {0} of {1} bytes")]
        public void ReadFromRedis(string key, long bytes, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(21, key, bytes, processName);
            }
        }

        [Event(22, Level = EventLevel.Verbose, Message = "{0} connecting to redis on {1} : {2}")]
        public void ConnectedToRedis(string address, bool success, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(22, pName ?? processName, address, success);
            }
        }

        [Event(23, Level = EventLevel.Verbose, Message = "{0} disconnected from redis {1}")]
        public void DisconnectedFromRedis(string address, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(23, pName ?? processName, address);
            }
        }

        [Event(24, Level = EventLevel.Verbose, Message = "{0} flush redis database {1}")]
        public void RedisFlushDatabase(int address, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(24, pName ?? processName, address);
            }
        }

        [Event(25, Level = EventLevel.Verbose, Message = "{2}: error writting to redis key {0}")]
        public void ErrorWritingToRedis(string key, string value, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(25, key, GetValue(value), pName ?? processName);
            }
        }

        [Event(26, Level = EventLevel.Verbose, Message = "{1}: error removing from redis key {0}")]
        public void ErrorRemoveFromRedis(string key, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(26, key, pName ?? processName);
            }
        }

        [Event(27, Level = EventLevel.Verbose, Message = "{1}: removing from redis key {0}")]
        public void RemoveFromRedis(string key, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(27, key, pName ?? processName);
            }
        }

        [Event(28, Level = EventLevel.Verbose, Message = "{2}: error removing value from redis key {0}")]
        public void ErrorRemoveValueFromRedis(string key, string value, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(28, key, GetValue(value), pName ?? processName);
            }
        }

        [Event(29, Level = EventLevel.Verbose, Message = "{2}: removing value from redis key {0}")]
        public void RemoveValueFromRedis(string key, string value, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(29, key, GetValue(value), pName ?? processName);
            }
        }

        [Event(30, Level = EventLevel.Verbose, Message = "{2}: CRM Read Directly Linked Claims for account {0}")]
        public void CRMReadAccountDirectlyLinkedClaims(string account, string claims, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(30, account, GetValue(claims), pName ?? processName);
            }
        }

        [Event(31, Level = EventLevel.Verbose, Message = "{2}: CRM Read Package Linked Claims for account {0}")]
        public void CRMReadAccountPackageLinkedClaims(string account, string claims, string pName = null)
        {
            if (IsEnabled())
            {
                WriteEvent(31, account, GetValue(claims), pName ?? processName);
            }
        }
    }
}
